/*  Vec3.h: General Purpose 3-D Vector class.
/*  EE563 - 3-D Graphics and Visualisation.
/*	General purpose float vector for use as vertices, vectors and normals.
*/

#if !defined _VEC3
#define _VEC3

#include<iostream>
using std::ostream;
#include<cfloat>
#include<cmath>  // for the isNaN (is not a number) function

class Vec3  
{
	public:		
		float v[3]; // states are public for ease of access

        Vec3() { v[0]=0.0f; v[1]=0.0f; v[2]=0.0f;}	// constructor
        Vec3(float x,float y,float z) { v[0]=x; v[1]=y; v[2]=z; }

		// overloaded operators
		inline bool operator == (const Vec3& v) const 
		  { return v[0]==v.v[0] && v[1]==v.v[1] && v[2]==v.v[2]; }
        
        inline bool operator != (const Vec3& v) const 
		  { return v[0]!=v.v[0] || v[1]!=v.v[1] || v[2]!=v.v[2]; }

		inline bool operator <  (const Vec3& v) const
        {
            if (v[0] < v.v[0]) return true;
            else if (v[0] > v.v[0]) return false;
            else if (v[1] < v.v[1]) return true;
            else if (v[1] > v.v[1]) return false;
            else return (v[2] < v.v[2]);
        }

        inline float* ptr() { return v; }
        inline const float* ptr() const { return v; }

        inline void set( float x, float y, float z)
        {
            v[0]=x; v[1]=y; v[2]=z;
        }

        inline float& operator [] (int i) { return v[i]; }
        inline float operator [] (int i) const { return v[i]; }

        inline float& x() { return v[0]; }
        inline float& y() { return v[1]; }
        inline float& z() { return v[2]; }

        inline float x() const { return v[0]; }
        inline float y() const { return v[1]; }
        inline float z() const { return v[2]; }

        inline bool valid() const { return !isNaN(); }
        inline bool isNaN() const { return _isnan(v[0]) || _isnan((double)v[1]) 
               || _isnan((double)v[2]); }

        
        inline float operator * (const Vec3& rhs) const // dot product
        {
            return v[0]*rhs.v[0]+v[1]*rhs.v[1]+v[2]*rhs.v[2];
        }
        
        inline const Vec3 crossProduct(const Vec3& rhs) const // cross product
        {   
            return Vec3(v[1]*rhs.v[2]-v[2]*rhs.v[1],
                v[2]*rhs.v[0]-v[0]*rhs.v[2] ,
                v[0]*rhs.v[1]-v[1]*rhs.v[0]);
        }
        inline const Vec3 operator ^ (const Vec3& rhs) const // cross product
        {
            return this->crossProduct(rhs);  // call method just above
        }

        inline const Vec3 operator * (float rhs) const // multiply by scalar
        {
            return Vec3(v[0]*rhs, v[1]*rhs, v[2]*rhs);
        }

        inline Vec3& operator *= (float rhs)
        {
            v[0]*=rhs; v[1]*=rhs; v[2]*=rhs; return *this;
        }

        inline const Vec3 operator / (float rhs) const // divide by scalar
        {
            return Vec3(v[0]/rhs, v[1]/rhs, v[2]/rhs);
        }

        inline Vec3& operator /= (float rhs) // divide by scalar
        {
            v[0]/=rhs; v[1]/=rhs; v[2]/=rhs; return *this;
        }

        inline const Vec3 operator + (const Vec3& rhs) const  // vector add
        {
            return Vec3(v[0]+rhs.v[0], v[1]+rhs.v[1], v[2]+rhs.v[2]);
        }

        inline Vec3& operator += (const Vec3& rhs)
        {
            v[0] += rhs.v[0]; v[1] += rhs.v[1]; 
            v[2] += rhs.v[2]; return *this;
        }

        inline const Vec3 operator - (const Vec3& rhs) const // vector subtract
        {
            return Vec3(v[0]-rhs.v[0], v[1]-rhs.v[1], v[2]-rhs.v[2]);
        }

        inline Vec3& operator -= (const Vec3& rhs) // vector subtract
        {
            v[0]-=rhs.v[0]; v[1]-=rhs.v[1]; v[2]-=rhs.v[2]; return *this;
        }

        inline const Vec3 operator - () const //negate
        {
            return Vec3 (-v[0], -v[1], -v[2]);
        }

        inline float length() const
        {
            return sqrtf( v[0]*v[0] + v[1]*v[1] + v[2]*v[2] );
        }

        inline float normalize()
        {
            float norm = Vec3::length();
            if (norm>0.0f)
            {
                v[0] /= norm; v[1] /= norm; v[2] /= norm;
            }                
            return( norm );
        }
        
        inline void zero() { v[0] = v[1] = v[2] = 0; }
        
        // to allow us to send our vector to the standard output stream cout
        friend inline ostream& operator << (ostream& output, const Vec3& vec)
        {
          output << "(" << vec.v[0] << "," << vec.v[1] << "," << vec.v[2] << ")";
          return output;        
        }
};

#endif // _VEC3
